﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;


using Microsoft.Kinect;
using System.Speech.Synthesis;
using MySql.Data.MySqlClient;   //引用MySql
using System.Collections;      //导入动态数组ArrayList的命名空间
using System.Windows.Navigation;   

namespace BodyTrain
{
    /// <summary>
    /// SwitchBalance.xaml 的交互逻辑
    /// </summary>
    public partial class SwitchBalance : Page
    {

        #region 定义私有属性
        //Kinect相关变量
        KinectSensor kinectSensor = null;
        CoordinateMapper coordinateMapper = null;
        MultiSourceFrameReader reader;
        IList<Body> bodies;
        CameraMode _mode = CameraMode.Color;

        //语音
        static SpeechSynthesizer speaker1 = new SpeechSynthesizer();//语音定义
        //只处理一个Body数据
        ulong bodyId = 666; //初始化要跟踪目标的bodyId。不能初始化为0的原因是，当检测到一个人时，BodyCount为6，多余的5个body相应的Id为0。有效的Id是个17位的正整数
        //姿势
        private Pose confirmPose; //确定姿势
        //挥手切换
        ArrayList locateLeft = new ArrayList(); //水平方向不能有较大移动
        ArrayList locateRight = new ArrayList();
        ArrayList handLeftX = new ArrayList();
        ArrayList handLeftZ = new ArrayList();
        ArrayList handRightX = new ArrayList();
        ArrayList handRightZ = new ArrayList();
        //识别切换到哪个动作
        int indexAction; //动作的索引号
        String[] actionName = { "单腿站立", "时钟练习", "错步练习" , "同侧抬起", "身体画圆" , "高抬腿", "侧走练习" , "障碍行走" , "障碍进阶" };

        #endregion

        public int IndexAction
        {
            get
            {
                return indexAction;
            }
            set
            {
                indexAction = value;
            }
        } //公有属性，设置indexAction数值


        public SwitchBalance()
        {
            InitializeComponent();//初始化窗口

            PopulatePoseLibrary();
        }
        #region 姿势判断相关函数
        private void PopulatePoseLibrary()//姿势库
        {
            //确定动作 Pose - 伸开双臂
            this.confirmPose = new Pose();
            this.confirmPose.Title = "伸开双臂(Start Messure)";
            this.confirmPose.Angles = new PoseAngle[4];
            this.confirmPose.Angles[0] = new PoseAngle(JointType.ShoulderLeft, JointType.ElbowLeft, 180, 20);
            this.confirmPose.Angles[1] = new PoseAngle(JointType.ElbowLeft, JointType.WristLeft, 180, 20);
            this.confirmPose.Angles[2] = new PoseAngle(JointType.ShoulderRight, JointType.ElbowRight, 0, 20);
            this.confirmPose.Angles[3] = new PoseAngle(JointType.ElbowRight, JointType.WristRight, 0, 20);
        }
        private void processGesture(Body body, Dictionary<JointType, Point> jointPoints)//姿势匹配
        {
            if (IsPose(body, this.confirmPose, jointPoints))      //检测到开始姿势，则开始
            {
                Confirm(); //界面跳转
            }

        }
        //判断是否和指定的姿势匹配
        private bool IsPose(Body body, Pose pose, IDictionary<JointType, Point> jointPoints)
        {
            bool isPose = true;
            double angle;
            double poseAngle;
            double poseThreshold;
            double loAngle;
            double hiAngle;

            for (int i = 0; i < pose.Angles.Length && isPose; i++)
            {
                poseAngle = pose.Angles[i].Angle;
                poseThreshold = pose.Angles[i].Threshold;
                angle = GetJointAngle(jointPoints, pose.Angles[i].CenterJoint, pose.Angles[i].AngleJoint);

                hiAngle = poseAngle + poseThreshold;
                loAngle = poseAngle - poseThreshold;

                if (hiAngle >= 360 || loAngle < 0)
                {
                    loAngle = (loAngle < 0) ? 360 + loAngle : loAngle;
                    hiAngle = hiAngle % 360;

                    isPose = !(loAngle > angle && angle > hiAngle);
                }
                else
                {
                    isPose = (loAngle <= angle && hiAngle >= angle);
                }
            }
            return isPose;
        }

        //获取骨骼点角度
        private double GetJointAngle(IDictionary<JointType, Point> jointPoints, JointType centerJoint, JointType angleJoint)
        {
            Point primaryPoint = jointPoints[centerJoint];
            Point anglePoint = jointPoints[angleJoint];
            Point x = new Point(primaryPoint.X + anglePoint.X, primaryPoint.Y);

            double a;
            double b;
            double c;

            a = Math.Sqrt(Math.Pow(primaryPoint.X - anglePoint.X, 2) + Math.Pow(primaryPoint.Y - anglePoint.Y, 2));
            b = anglePoint.X;
            c = Math.Sqrt(Math.Pow(anglePoint.X - x.X, 2) + Math.Pow(anglePoint.Y - x.Y, 2));

            double angleRad = Math.Acos((a * a + b * b - c * c) / (2 * a * b));
            double angleDeg = angleRad * 180 / Math.PI;

            if (primaryPoint.Y < anglePoint.Y)
            {
                angleDeg = 360 - angleDeg;
            }
            return angleDeg;
        }

        #endregion

        #region Kinect相关函数
        //相机模式，彩色、深度、红外
        private enum CameraMode
        {
            Color,
            Depth,
            Infrared
        }

        private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            kinectSensor = KinectSensor.GetDefault();
            coordinateMapper = kinectSensor.CoordinateMapper;
            if (kinectSensor != null)
            {
                kinectSensor.Open();
                this.reader = kinectSensor.OpenMultiSourceFrameReader(FrameSourceTypes.Color | FrameSourceTypes.Depth | FrameSourceTypes.Infrared | FrameSourceTypes.Body);
                this.reader.MultiSourceFrameArrived += reader_MultiSourceFrameArrived;
            }
            //语音设置
            speaker1.SelectVoiceByHints(VoiceGender.Female, VoiceAge.Adult, 2, System.Globalization.CultureInfo.CurrentCulture);
            speaker1.Rate = -2;
            speaker1.Volume = 100;

            remind1.Text = actionName[indexAction]; //界面刚跳转时，显示是从哪个动作跳转而来

        }

        private void Page_Closed(object sender, RoutedEventArgs e)
        {

            //释放资源？
            if (this.reader != null)
            {
                // ColorFrameReder is IDisposable接口
                this.reader.Dispose();
                this.reader = null;
            }
            //关闭Kinect传感器
            if (this.kinectSensor != null)
            {
                this.kinectSensor.Close();
                this.kinectSensor = null;
            }
        }

        void reader_MultiSourceFrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
        {
            var reference = e.FrameReference.AcquireFrame();
            //彩色
            using (var frame = reference.ColorFrameReference.AcquireFrame())
            {
                if (frame != null)
                {
                    if (_mode == CameraMode.Color)
                    {
                        camera.Source = frame.ToBitmap();
                    }
                }
            }
            // Body
            using (var frame = reference.BodyFrameReference.AcquireFrame())
            {
                if (frame != null)
                {
                    canvas.Children.Clear();

                    bodies = new Body[frame.BodyFrameSource.BodyCount]; //获取当前帧所有人骨骼点数据

                    frame.GetAndRefreshBodyData(bodies); //获取刷新的身体数据

                    TrackOne(bodies, frame); //获取满足条件的BodyId

                    foreach (var body in bodies) //当捕获到多人骨骼点时，一个个遍历
                    {
                        //if (body.IsTracked)     //骨骼点被跟踪
                        if (body.IsTracked && (bodyId == body.TrackingId)) //骨骼点被跟踪，且相应的Id是跟踪目标
                        {

                            canvas.DrawSkeleton(body, coordinateMapper);//在canvas中绘制骨骼

                            IReadOnlyDictionary<JointType, Joint> joints = body.Joints;
                            Dictionary<JointType, Point> jointPoints = new Dictionary<JointType, Point>();
                            foreach (JointType jointType in body.Joints.Keys)
                            {
                                jointPoints[jointType] = joints[jointType].ScaleTo(coordinateMapper);
                            }


                            processGesture(body, jointPoints); //匹配确定姿势，进行界面跳转

                            Movement(body);
                        }
                    }
                }
            }
        }

        #endregion

        //左右挥手识别
        private void Movement(Body body)
        {
            Joint tempLeft = body.Joints[JointType.HandTipLeft];
            Joint tempRight = body.Joints[JointType.HandTipRight];
            Joint tempLoc = body.Joints[JointType.SpineMid];

            if (handLeftX.Count < 36) //限制动态数组大小，当超过大小时，丢弃一部分数据之后，重新记录数据
            {
                handLeftX.Add(tempLeft.Position.X);
                handLeftZ.Add(tempLeft.Position.Z);

                locateLeft.Add(tempLoc.Position.X);            
            }
            else
            {
                handLeftX.RemoveRange(0, 10);
                handLeftZ.RemoveRange(0, 10);
                handLeftX.Add(tempLeft.Position.X);
                handLeftZ.Add(tempLeft.Position.Z);
                handLeftX.TrimToSize();
                handLeftZ.TrimToSize();

                locateLeft.RemoveRange(0, 10);
                locateLeft.Add(tempLoc.Position.X);
                locateLeft.TrimToSize();
            }

            if (handRightX.Count < 36)
            {
                handRightX.Add(tempRight.Position.X);
                handRightZ.Add(tempRight.Position.Z);

                locateRight.Add(tempLoc.Position.X);
            }
            else
            {
                handRightX.RemoveRange(0, 10);
                handRightZ.RemoveRange(0, 10);
                handRightX.Add(tempRight.Position.X);
                handRightZ.Add(tempRight.Position.Z);
                handRightX.TrimToSize();
                handRightZ.TrimToSize();

                locateRight.RemoveRange(0, 10);
                locateRight.Add(tempLoc.Position.X);
                locateRight.TrimToSize();
            }


            MoveLeft();
            MoveRight();


        }

        //判断向左挥手
        private void MoveLeft()
        {
            if (handLeftX.Count >= 18) //左挥手判断
            {
                ArrayList tempHandLeftX = new ArrayList(handLeftX);
                tempHandLeftX.Sort();
                float mix = (float)tempHandLeftX[0];
                float max = (float)tempHandLeftX[tempHandLeftX.Count - 1];
                int mix_index = handLeftX.IndexOf(mix);
                int max_index = handLeftX.IndexOf(max);

                //手移动的距离大于0.4m，且手开始挥时在位置点左侧，挥出后超过位置点一定距离。
                if (((max - mix) > 0.38f) && (mix_index < (max_index - 10)) && ((float)handLeftX[mix_index] < (float)locateLeft[mix_index]) && ((float)handLeftX[max_index] > ((float)locateLeft[max_index] + 0.12f)))
                {
                    //通过左手Z轴值判断人是否距离Kinect过近
                    if (((float)handLeftZ[mix_index] > 1.0f) && ((float)handLeftZ[max_index] > 1.0f))
                    {
                        handLeftX.Clear();
                        handLeftX.TrimToSize();
                        handLeftZ.Clear();
                        handLeftZ.TrimToSize();
                        locateLeft.Clear();
                        locateLeft.TrimToSize();

                        if (indexAction == 0)
                        {
                            indexAction = 8;
                            speaker1.SpeakAsync(actionName[indexAction]); //语音提示切换到什么动作
                            remind1.Text = actionName[indexAction]; //文本显示，当前是什么动作
                        }
                        else
                        {
                            indexAction = indexAction - 1;
                            speaker1.SpeakAsync(actionName[indexAction]); //语音提示切换到什么动作
                            remind1.Text = actionName[indexAction]; //文本显示，当前是什么动作
                        }
                        
                    }


                }
            }
        }

        //判断向右挥手
        private void MoveRight()
        {
            if (handRightX.Count >= 18) //右挥手判断
            {
                ArrayList tempHandRightX = new ArrayList(handRightX);

                tempHandRightX.Sort();
                float mix = (float)tempHandRightX[0];
                float max = (float)tempHandRightX[tempHandRightX.Count - 1];
                int mix_index = handRightX.IndexOf(mix);
                int max_index = handRightX.IndexOf(max);

                //手移动的距离大于0.4m，且手开始挥时在位置点右侧，挥出后超过位置点一定距离。
                if (((max - mix) > 0.38f) && ((mix_index - 10) > (max_index)) && ((float)handRightX[mix_index] < ((float)locateRight[mix_index] - 0.12f)) && ((float)handRightX[max_index] > (float)locateRight[max_index]))
                {
                    //通过右手Z轴值判断人是否距离Kinect过近
                    if (((float)handRightZ[mix_index] > 1.0f) && ((float)handRightZ[max_index] > 1.0f))
                    {
                        handRightX.Clear();
                        handRightX.TrimToSize();
                        handRightZ.Clear();
                        handRightZ.TrimToSize();
                        locateRight.Clear();
                        locateRight.TrimToSize();

                        if (indexAction == 8)
                        {
                            indexAction = 0;
                            speaker1.SpeakAsync(actionName[indexAction]); //语音提示切换到什么动作
                            remind1.Text = actionName[indexAction]; //文本显示，当前是什么动作
                        }
                        else
                        {
                            indexAction = indexAction + 1;
                            speaker1.SpeakAsync(actionName[indexAction]); //语音提示切换到什么动作
                            remind1.Text = actionName[indexAction]; //文本显示，当前是什么动作
                        }
                        
                    }
                }

            }
        }

        //界面跳转
        private void Confirm()
        {
            switch (indexAction)
            {
                case 0:
                    //StandOneLeg standOneLeg = new StandOneLeg();
                    //this.Close();
                    //standOneLeg.Show();
                    NavigationService.GetNavigationService(this).Navigate(new Uri("/BalanceTrain/StandOneLeg.xaml", UriKind.Relative)); 
                    break;
                case 1:
                    //ClockTrain clockTrain = new ClockTrain();
                    //this.Close();
                    //clockTrain.Show();        
                    NavigationService.GetNavigationService(this).Navigate(new Uri("/BalanceTrain/ClockTrain.xaml", UriKind.Relative));
                    break;
                case 2:
                    //StepByStep stepByStep = new StepByStep();
                    //this.Close();
                    //stepByStep.Show();
                    NavigationService.GetNavigationService(this).Navigate(new Uri("/BalanceTrain/StepByStep.xaml", UriKind.Relative));
                    break;
                case 3:
                    //RaiseSameSide raiseSameSide = new RaiseSameSide();
                    //this.Close();
                    //raiseSameSide.Show();
                    NavigationService.GetNavigationService(this).Navigate(new Uri("/BalanceTrain/RaiseSameSide.xaml", UriKind.Relative));
                    break;
                case 4:
                    //PaintCircle paintCircle = new PaintCircle();
                    //this.Close();
                    //paintCircle.Show();
                    NavigationService.GetNavigationService(this).Navigate(new Uri("/BalanceTrain/PaintCircle.xaml", UriKind.Relative));
                    break;
                case 5:
                    //HighKnee highKnee = new HighKnee();
                    //this.Close();
                    //highKnee.Show();
                    NavigationService.GetNavigationService(this).Navigate(new Uri("/BalanceTrain/HighKnee.xaml", UriKind.Relative));
                    break;
                case 6:
                    //SideWalk sideWalk = new SideWalk();
                    //this.Close();
                    //sideWalk.Show();
                    NavigationService.GetNavigationService(this).Navigate(new Uri("/BalanceTrain/SideWalk.xaml", UriKind.Relative));
                    break;
                case 7:
                    //BarrierWalk barrierWalk = new BarrierWalk();
                    //this.Close();
                    //barrierWalk.Show();
                    NavigationService.GetNavigationService(this).Navigate(new Uri("/BalanceTrain/BarrierWalk.xaml", UriKind.Relative));
                    break;
                case 8:
                    //AdvancedBarrier advancedBarrier = new AdvancedBarrier();
                    //this.Close();
                    //advancedBarrier.Show();
                    NavigationService.GetNavigationService(this).Navigate(new Uri("/BalanceTrain/AdvancedBarrier.xaml", UriKind.Relative)); 
                    break;
            }
        }


        //获取BodyId，便于之后实现只跟踪一个人的目标 
        private void TrackOne(IList<Body> bodies, BodyFrame frame)  //只处理一人数据
        {
            //这个Id选取站立在图像中心人的bodyId。当丢失这个Id时，再次获取图像中心人的bodyId。
            //判断人是否在于中心，是利用SpineMid骨骼点的水平坐标值(X轴坐标)，判断其绝对值是否小于0.16。
            //kinect中心是X轴的原点

            int bodyNum = frame.BodyFrameSource.BodyCount; //获得图像帧中身体数目
            ArrayList bodyIds = new ArrayList(); //存放图像帧中bodyId
            int i = 0;
            float temp = 666; //临时存放水平坐标值 

            for (i = 0; i < bodyNum; i++) //记录bodyId
            {
                bodyIds.Add(bodies[i].TrackingId); //存放bodyId
            }

            if (bodyIds.Contains(bodyId)) //判断bodyId是否在该图像帧
            {

            }
            else //不在图像帧中
            {
                for (i = 0; i < bodyNum; i++)
                {
                    if (bodies[i].TrackingId > 666) //剔除无效的Id
                    {
                        temp = bodies[i].Joints[JointType.SpineMid].Position.X;

                        if ((Math.Abs(temp) < 0.16f) && bodies[i].IsTracked) //水平坐标绝对值小于阈值，且相应的身体被追踪
                        {
                            bodyId = bodies[i].TrackingId;
                            break;
                        }
                    }
                }
            }
        }
        private void GoBack_Click(object sender, RoutedEventArgs e)
        {
            NavigationService.GetNavigationService(this).Navigate(new Uri("/BalanceTrain/BalanceTrain.xaml", UriKind.Relative));
        }



    }
}
